package com.dingwen.treasure.beansearch.controller;

import com.dingwen.treasure.base.enums.LogicDeleteEnum;
import com.dingwen.treasure.base.util.ReflectUtil;
import com.dingwen.treasure.base.pojo.vo.Result;
import com.dingwen.treasure.base.pojo.vo.ResultGenerator;
import com.ejlchina.searcher.BeanSearcher;
import com.ejlchina.searcher.SearchResult;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;

import java.io.Serializable;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;

/**
 * 　AbstractBaseControllerBeanSearchImpl: 基于 BeanSearch 实现的通用搜素接口 </br>
 * <p> 接口路径： </p>
 * <ol>
 *     <li> 1. search/all   POST      body   搜索所有符合条件的列表 </li>
 *     <li> 2. search/page   POST      body   分页搜索所有符合条件的列表 </li>
 *     <li> 3. search/count   POST      body  搜索符合条件的记录条数 </li>
 *     <li> 4. search/first   POST      body   搜索符合条件的第一条记录 </li>
 * </ol>
 * 　@author dingwen
 * 　@date 2022/7/2
 */
@Slf4j
public abstract class AbstractBaseControllerBeanSearchImpl<P extends Serializable> {

    /**
     * po
     */
    protected volatile P po;

    /**
     * 是否启用逻辑删除，默认开启
     */
    protected boolean enableLogicDelete = Boolean.TRUE;

    /**
     * 逻辑删除实体属性名称
     */
    protected String logicDeleteName = "deleted";

    /**
     * 逻辑删除时表示存在的值
     */
    protected String logicDeleteValue = LogicDeleteEnum.EXISTED.getCode().toString();

    /**
     * beanSearcher 基础 search 服务
     */
    @Autowired
    protected BeanSearcher beanSearcher;

    /* --------------------------------------------搜索--------------------------------------------*/

    /**
     * 搜索列表； 搜索不做VO视图对象处理，具体返回搜索的数据内容通过SearchBean进行构建 </br>
     * <pre>
     *     1. MybatisPlus 的逻辑删除失效需要自己实现
     *     2. 枚举翻译借助 MybatisPlus 实现
     * </pre>
     *
     * <p> 参数使用示例：op: 操作类型，默认存在field-0、field-op: eq</p>
     * <pre>
     *    {
     *       "password-op":"ey",                        password is not empty （ny：不为空）
     *
     *       "version": 1,
     *       "version-op":"eq",                         version = 1
     *
     *       "gender": 0,
     *       "gender-op":"ge",                          gender >= 0
     *
     *       "status": 1,
     *       "status-op": "le",                         status <= 1
     *
     *       gt >
     *       lt <
     *
     *       "createTime-0":"2003-09-02 19:04:40",      createTime 2003-09-02 19:04:40 between and 2004-09-02 19:04:40
     *       "createTime-1":"2004-09-02 19:04:40",
     *       "createTime-op":"bt",
     *
     *       "nickname": "a",
     *       "nickname-op":"ct",                        nickname like %a%
     *
     *        sw %a
     *        ew a%
     *
     *       "id-0": 579,
     *       "id-1": 660,
     *       "id-2": 297,
     *       "id-op": "il",                             id in(579,660,297)
     *
     *       "sort":"lastLoginTime",
     *       "order":"desc" ,                           order by lastLoginTime desc
     *
     *       "onlySelect": "id,name",                    只显示指定字段
     *       "selectExclude":"password",                  排除显示密码
     *       "name-ic": true                             忽略大小写
     *    }
     * </pre>
     *
     * @param params 搜索参数
     * @return {@link Result}
     */

    @ApiOperation(value = "搜索所有符合条件的列表", response = Result.class)
    @ApiImplicitParam(name = "params", value = "请求参数", paramType = "body")
    @PostMapping("search-all")
    public Result<?> searchAll(@RequestBody Map<String, Object> params) {
        logicDeleteHandler(params);
        // 获取数据持久对象
        P po = getPo();
        // 调用 Bean Search 执行检索
        List searchResult = beanSearcher.searchAll(po.getClass(), params);
        return ResultGenerator.genSuccessResult(searchResult);
    }


    /**
     * 分页搜索所有符合条件的列表（支持单表、多表联查）
     *
     * @param params 参数
     * @return {@link Result}
     */
    @ApiOperation(value = "分页搜索所有符合条件的列表", response = Result.class)
    @ApiImplicitParam(name = "params", value = "请求参数", paramType = "body")
    @PostMapping("search-page")
    public Result<SearchResult> search(@RequestBody Map<String, Object> params) {
        logicDeleteHandler(params);
        // 获取数据持久对象
        P po = getPo();
        // 调用 Bean Search 执行检索
        SearchResult searchResult = beanSearcher.search(po.getClass(), params);
        return ResultGenerator.genSuccessResult(searchResult);
    }

    /**
     * 搜索符合条件的第一条记录
     *
     * @param params 参数
     * @return {@link Result}
     */
    @ApiOperation(value = "搜索符合条件的第一条记录", response = Result.class)
    @ApiImplicitParam(name = "params", value = "请求参数", paramType = "body")
    @PostMapping("search-first")
    public Result searchFirst(@RequestBody Map<String, Object> params) {
        logicDeleteHandler(params);
        // 获取数据持久对象
        P po = getPo();
        Object result = beanSearcher.searchFirst(po.getClass(), params);
        return ResultGenerator.genSuccessResult(result);
    }

    /**
     * 搜索符合条件的记录条数
     *
     * @param params 参数
     * @return {@link Result}
     */
    @ApiOperation(value = "搜索符合条件的记录条数", response = Result.class)
    @ApiImplicitParam(name = "params", value = "请求参数", paramType = "body")
    @PostMapping("search-count")
    public Result searchCount(@RequestBody Map<String, Object> params) {
        logicDeleteHandler(params);
        // 获取数据持久对象
        P po = getPo();
        Number number = beanSearcher.searchCount(po.getClass(), params);
        return ResultGenerator.genSuccessResult(number);
    }

    /**
     * 判断是否需要添加逻辑删除的条件: 注意此处函数修改了上级函数的的params
     *
     * @param params 参数
     * @return {@link Optional}<{@link Class}>
     */
    private synchronized void logicDeleteHandler(Map<String, Object> params) {
        // 添加逻辑删除
        if (enableLogicDelete) {
            params.put(logicDeleteName, logicDeleteValue);
        }
    }

    /**
     * 获取PO
     *
     * @return {@link P} po 持久化对象
     */
    @SneakyThrows(Throwable.class)
    public P getPo() {
        if (!Objects.isNull(po)) {
            return po;
        }
        Class<P> poClass = ReflectUtil.getClassForGeneric(0, this);
        po = poClass.newInstance();
        return po;
    }
}
